﻿/****************************************************************************************
 * Copyright © 2020 - 2023 by AngryKeyboard.
 * SFML_俄罗斯方块 is licensed under MulanOWL BY v1.
 * You can use this software according to the terms and conditions of the MulanOWL BY v1.
 * You may obtain a copy of MulanOWL BY v1 at:
 *          http://license.coscl.org.cn/MulanOWLBYv1
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the MulanOWL BY v1 for more details.
****************************************************************************************/
#include <iostream>
#include "CBlock.h"


CBlock::CBlock() {
  //形状随机，取值1~BLOCK_TYPE_COUNT
  m_nShape = rand() % BLOCK_TYPE_COUNT;

  //测试用
  //m_nShape = I;
  //m_nShape = L;
  //m_nShape = J;
  //m_nShape = T;
  //m_nShape = S;
  //m_nShape = Z;
  //m_nShape = O;

  for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
    //初始化每个点的坐标
    m_Pos[i].row = BLOCKS[m_nShape][i] % 2;
    m_Pos[i].column = BLOCKS[m_nShape][i] / 2;
  }
  //初始化方向为上
  m_nDirection = UP;

  //平移到游戏区域的中间位置
  //4是俄罗斯方块的最大宽度
  move(RIGHT, (COL_COUNT - 4) / 2);
  move(DOWN, -2);
}

CBlock::CBlock(int Shape) {
  m_nShape = Shape;

  for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
    //初始化每个点的坐标
    m_Pos[i].row = BLOCKS[m_nShape][i] % 2;
    m_Pos[i].column = BLOCKS[m_nShape][i] / 2;
  }
  //初始化方向为上
  m_nDirection = UP;

  //平移到游戏区域的中间位置
  //4是俄罗斯方块的最大宽度
  move(RIGHT, (COL_COUNT - 4) / 2);
  move(DOWN, -2);
}

CBlock::CBlock(const CBlock &block) {
  //复制方块数据
  m_nShape = block.m_nShape;
  m_nDirection = block.m_nDirection;
  memcpy(m_Pos, block.m_Pos, sizeof(m_Pos));
}

bool CBlock::ifOverBottom(const int table[ROW_COUNT][COL_COUNT]) {
  for (int i = 0; i < 4; i++) {
    int row = m_Pos[i].row;
    int col = m_Pos[i].column;
    //检查行列数，避免访问table的时候访问到非法内存
    if (row < 0) {
      row = 0;
    }
    if (col < 0) {
      col = 0;
    }

    //如果该点的行坐标超过底部，或游戏桌面的已经填充过方块
    if (m_Pos[i].row >= ROW_COUNT || table[row][col]) {
      return true;//越界
    }
  }

  return false;
}

void CBlock::normalize() {
  int minCol = COL_COUNT; //左边缘列坐标
  int maxCol = 0;     //右边缘列坐标

  //找出方块左右边缘的坐标
  for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
    int curCol = m_Pos[i].column;
    if (curCol < minCol) {
      minCol = curCol;
    }
    if (curCol >= maxCol) {
      maxCol = curCol;
    }
  }

  //处理越界
  while (minCol < 0) {
    //方块右移
    move(RIGHT);
    minCol++;
  }
  while (maxCol >= COL_COUNT) {
    //方块左移
    move(LEFT);
    maxCol--;
  }
}

bool CBlock::drop1Step(const int table[ROW_COUNT][COL_COUNT]) {
  CBlock bakBlock(*this);//备份当前方块

  move(DOWN); //当前方块下落一格
  if (ifOverBottom(table)) {
    //如果下落一格后，与底部方块重叠或超越下边界
    //就恢复备份，并返回true（越界）
    *this = bakBlock;
    return true;
  }

  return false;//（不越界）
}

void CBlock::dropToBottom(const int table[ROW_COUNT][COL_COUNT]) {
  //不越界的时候一直下落一格
  while (drop1Step(table) == false);
}

void CBlock::moveLeftRight(const int table[ROW_COUNT][COL_COUNT], int offset) {
  CBlock bakBlock(*this);//备份当前方块

  move(RIGHT, offset);

  normalize();//左右越界处理

  //底部方块重合处理
  if (ifOverBottom(table)) {
    *this = bakBlock;
  }
}

void CBlock::rotate(const int table[ROW_COUNT][COL_COUNT], int rotateDirection) {
  if (m_nShape == O) {
    // O形，不需要旋转
    return;
  }

  CBlock bakBlock(*this);//备份当前方块

  //旋转
  switch (rotateDirection) {
    case CLOCKWISE:
      rotateClockwise();
      break;
    case ANTICLOCKWISE:
      rotateAnticlockwise();
      break;
    default:
      break;
  }

  //如果方块旋转后超过下边界，就还原
  if (ifOverBottom(table)) {
    *this = bakBlock;
    return; //直接退出，后续语句不执行
  }

  m_nDirection += rotateDirection;        //刷新方向数据
  m_nDirection += DIRECTION_COUNT;        //保证方向数据不为负数
  m_nDirection = m_nDirection % DIRECTION_COUNT;  //保证方向数据不超过指定范围

  normalize();//处理旋转造成的左右越界
}

void CBlock::move(int direction, int step) {
  switch (direction) {
    case RIGHT:
      for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
        m_Pos[i].column += step;
      }
      break;
    case DOWN:
      for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
        m_Pos[i].row += step;
      }
      break;
    case LEFT:
      for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
        m_Pos[i].column -= step;
      }
      break;
    default:
      break;
  }
}

int CBlock::Shape() const {
  return m_nShape;
}

int CBlock::Direction() const {
  return m_nDirection;
}

const SPoint *CBlock::Pos() const {
  return m_Pos;
}

void CBlock::rotateClockwise() {
  SPoint p = m_Pos[1]; //center of rotation
  if (m_nShape == I) {
    //针对I形方块优化
    switch (m_nDirection) {
      case UP:
      case RIGHT:
        p = m_Pos[2];
        break;
      case DOWN:
      case LEFT:
        p = m_Pos[1];
        break;
      default:
        break;
    }
  }

  //顺时针旋转
  for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
    SPoint tmp = m_Pos[i];
    m_Pos[i].column = p.column - (tmp.row - p.row);
    m_Pos[i].row = p.row + (tmp.column - p.column);
  }
}

void CBlock::rotateAnticlockwise() {
  SPoint p = m_Pos[1]; //center of rotation
  if (m_nShape == I) {
    //针对I形方块优化
    switch (m_nDirection) {
      case UP:
      case LEFT:
        p = m_Pos[1];
        break;
      case DOWN:
      case RIGHT:
        p = m_Pos[2];
        break;
      default:
        break;
    }
  }

  //逆时针旋转
  for (int i = 0; i < BLOCK_POINT_COUNT; i++) {
    SPoint tmp = m_Pos[i];
    m_Pos[i].column = p.column + (tmp.row - p.row);
    m_Pos[i].row = p.row - (tmp.column - p.column);
  }
}
